/****************************************************************************
 * Copyright (C) 2013 Dj_Skual
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ****************************************************************************/
#include "DownloadWiiSaveTask.hpp"
#include "../../Controls/Application.h"
#include "../../FileOperations/fileops.h"
#include "../../Network/http.h"
#include "../../Network/HTML_Stream.h"
#include "../../FileOperations/DirList.h"
#include "../DataBin/WiiSaveCrypted.h"
#include "ExtractWiiSaveTask.hpp"

DownloadWiiSaveTask::DownloadWiiSaveTask(const std::string &Destpath, u64 Tid,const std::string &ID, WiiSave_List * SaveList, int SelectedSave)
	: ManageProgressWindow(tr("Downloading file..."), "www.wiisave.com")
	, destpath(Destpath)
	, tid(Tid)
	, id(ID)
	, saveList(SaveList)
	, selectedSave(SelectedSave)
	, canceled(false)
{
	Canceled.connect(this, &DownloadWiiSaveTask::OnCancelClick);
	ThreadedTaskHandler::Instance()->AddTask(this);
}

void DownloadWiiSaveTask::OnCancelClick()
{
	canceled = true;
}

int DownloadWiiSaveTask::Download()
{
	struct block file;
	FILE * File = NULL;
	std::string url = saveList->GetDownloadLink(selectedSave);
	
	HTML_Stream HTML(url.c_str());

	HTML.FindStringEnd("location='");
	char * location = HTML.CopyString("'");
	if(location && !canceled)
	{
		url = url.erase(23);
		url += location;
		
		free(location);
	}
	else
	{
		if(canceled)
			return 0;
		else
			return -1;
	}
	
	file = downloadfile(url.c_str());
	if (!file.data || !file.size || canceled)
	{
		if(canceled)
			return 0;
		else
			return -1;
	}

	dlFilepath = destpath;
	if(dlFilepath[dlFilepath.size()-1] != '/')
		dlFilepath += "/";
	dlFilepath += saveList->GetFilename(selectedSave);
	
	File = fopen(dlFilepath.c_str(), "wb");
	if(!File)
	{
		return -1;
	}
	
	fwrite(file.data, 1, file.size, File);
	fclose(File);
	
	if(canceled)
	{
		RemoveFile(dlFilepath);
		return 0;
	}
	
	return 1;
}

int DownloadWiiSaveTask::Extract()
{
	SetTitle(tr("Extracting files..."));
	SetMessage("");
	
	std::string extractPath = destpath;
	if(extractPath[extractPath.size()-1] != '/')
		extractPath += "/";
	extractPath += "tmpExtract";
	
	ExtractWiiSaveTask * task = new ExtractWiiSaveTask(dlFilepath, extractPath);
	while(task->IsRunning())
		Application::Instance()->updateEvents();
	int result = task->GetResult();
	
	if(canceled)
	{
		RemoveDirectory(extractPath);
		result = 0;
	}
	else if(result < 0)
	{
		result = -7;
		RemoveDirectory(extractPath);
	}
	
	RemoveFile(dlFilepath);
	dlFilepath = extractPath;
	
	return (result > 0 ? Check() : result);
}

int DownloadWiiSaveTask::Check()
{
	SetTitle(tr("Verifying file..."));
	SetMessage("");
	
	type = NONE;
	
	if(dlFilepath.rfind(".") == std::string::npos)
	{
		DirList * dirList = new DirList(dlFilepath, ".bin" , DirList::Files | DirList::Dirs | DirList::CheckSubfolders);
		if(dirList)
		{
			for(int i = 0; i < dirList->GetFilecount(); i++)
			{
				if(dirList->IsDir(i))
					continue;
				
				if(!strcmp(dirList->GetFilename(i), "banner.bin"))
				{
					type = UNCOMPRESSED;
					srcFilepath = dirList->GetFilepath(i);
					break;
				}
				
				std::string name(dirList->GetFilename(i));
				if(!CompareBinaryFiles(name.substr(name.rfind(".")).c_str()))
				{
					_SaveCryptedInfos * infos = WiiSaveCrypted::GetInfos(dirList->GetFilepath(i));
					if(infos)
					{
						type = COMPRESSED;
						srcFilepath = dirList->GetFilepath(i);
						delete infos;
						break;
					}
				}
			}
			delete dirList;
		}
	}
	else
	{
		_SaveCryptedInfos * infos = WiiSaveCrypted::GetInfos(dlFilepath);
		if(infos)
		{
			type = COMPRESSED;
			srcFilepath = dlFilepath;
			delete infos;
		}
	}
	
	if(canceled)
		type = CANCELED;
	
	if(type <= CANCELED)
	{
		RemoveFile(dlFilepath);
		RemoveDirectory(dlFilepath);
	}
	
	return type;
}

int DownloadWiiSaveTask::Write()
{
	SetTitle(tr("Writing files to:"));
	SetMessage(destpath);
	
	int result = -9;
	
	std::string finaldest = destpath;
	if(finaldest[finaldest.size()-1] != '/')
		finaldest += "/";
	
	if(type == COMPRESSED)
	{
		finaldest += id;
		
		if(!CreateSubfolder(finaldest))
			goto end;
		
		finaldest += "/data.bin";
		result = CopyFile(srcFilepath.c_str(), finaldest.c_str());
	}
	else if(type == UNCOMPRESSED)
	{
		DirList * dirList = new DirList(srcFilepath, NULL, DirList::Files | DirList::Dirs);
		if(!dirList)
			goto end;
		
		finaldest += fmt("%lld", tid);
		
		if(!CreateSubfolder(finaldest))
		{
			delete dirList;
			goto end;
		}
		
		bool succes = true;
		for(int i = 0; succes && i < dirList->GetFilecount(); i++)
		{
			if(dirList->IsDir(i))
				succes = (CopyDirectory(dirList->GetFilepath(i), finaldest.c_str()) > 0);
			else
				succes = (CopyFile(dirList->GetFilepath(i),  fmt("%s/%s", finaldest.c_str(), dirList->GetFilename(i))) > 0);
		}
		
		if(succes)
			result = 1;
		
		delete dirList;
	}
	
  end:
	if(canceled)
		result = 0;
	
	if(result <= 0)
	{
		RemoveFile(finaldest);
		RemoveDirectory(finaldest);
	}
	
	RemoveFile(dlFilepath);
	RemoveDirectory(dlFilepath);
	
	return result;
}

void DownloadWiiSaveTask::Execute(void)
{
	int result = Download();
	if(result <= 0)
	{
		if(result < 0)
			result = -6;
		goto end;
	}
	
	if(!CompareArchive(dlFilepath.substr(dlFilepath.rfind(".")).c_str())) //! archive
	{
		result = Extract();
	}
	else //! check for data.bin file
	{
		result = Check();
	}
	
	if(result <= 0)
		goto end;
	
	result = Write();
	
  end:
	TaskEnd(result);
	
	Application::Instance()->PushForDelete(this);
}
